/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "prepared_statement.h"
#pragma warning(disable:4996)

namespace node {
	namespace rdb {

		StmtParameters::StmtParameters(::std::uint32_t nParams)
		{
			// reserve memory if needed
			if (nParams > 0)
			{
				params_.reserve(nParams);
			}
		}

		void StmtParameters::Reset(const Statement& stmt)
		{
			params_.clear();
			// reserve memory if needed
			if (stmt.get_arguments_count() > 0)
			{
				params_.reserve(stmt.get_arguments_count());
			}
		}

		//////////////////////////////////////////////////////////////////////////
		Statement& Statement::operator=(const Statement& index)
		{
			if (this != &index)
			{
				index_ = index.index_;
				db_ = index.db_;

				delete params_;
				params_ = NULL;

				if (index.params_)
				{
					params_ = new StmtParameters(*(index.params_));
				}
			}

			return *this;
		}

		bool Statement::Execute()
		{
			StmtParameters* args = Detach();
			// verify amount of bound parameters
			if (args->GetBoundParamCount() != get_arguments_count())
			{
				//sLog.outError("SQL ERROR: wrong amount of parameters (%i instead of %i)", args->boundParams(), arguments());
				//sLog.outError("SQL ERROR: statement: %s", m_pDB->GetStmtString(ID()).c_str());
				//MANGOS_ASSERT(false);
				return false;
			}

			return db_->ExecuteStmt(index_, args);
		}

		bool Statement::DirectExecute()
		{
			StmtParameters* args = Detach();
			// verify amount of bound parameters
			if (args->GetBoundParamCount() != get_arguments_count())
			{
				//sLog.outError("SQL ERROR: wrong amount of parameters (%i instead of %i)", args->boundParams(), arguments());
				//sLog.outError("SQL ERROR: statement: %s", m_pDB->GetStmtString(ID()).c_str());
				//MANGOS_ASSERT(false);
				return false;
			}

			return db_->DirectExecuteStmt(index_, args);
		}

		//////////////////////////////////////////////////////////////////////////
		PlainPreparedStatement::PlainPreparedStatement(const std::string& fmt, Connection& conn) : PreparedStatement(fmt, conn)
		{
			prepared_ = true;
			params_ = std::count(format_.begin(), format_.end(), '?');
			query_ = strnicmp(format_.c_str(), "select", 6) == 0;
		}

		void PlainPreparedStatement::bind(const StmtParameters& holder)
		{
			// verify if we bound all needed input parameters
			if (params_ != holder.GetBoundParamCount())
			{
				//MANGOS_ASSERT(false);
				return;
			}

			// reset resulting plain SQL request
			plain_request_ = format_;
			size_t nLastPos = 0;

			StmtParameters::ParameterContainer const& _args = holder.get_params();

			StmtParameters::ParameterContainer::const_iterator iter_last = _args.end();
			for (StmtParameters::ParameterContainer::const_iterator iter = _args.begin(); iter != iter_last; ++iter)
			{
				// bind parameter
				const StmtFieldData& data = (*iter);

				std::ostringstream fmt;
				DataToString(data, fmt);

				nLastPos = plain_request_.find('?', nLastPos);
				if (nLastPos != std::string::npos)
				{
					std::string tmp = fmt.str();
					plain_request_.replace(nLastPos, 1, tmp);
					nLastPos += tmp.length();
				}
			}
		}

		bool PlainPreparedStatement::execute()
		{
			if (plain_request_.empty())
			{
				return false;
			}

			return conn_.Execute(plain_request_.c_str());
		}

		void PlainPreparedStatement::DataToString(const StmtFieldData& data, std::ostringstream& fmt)
		{
			switch (data.get_type())
			{
			case FIELD_BOOL:    fmt << "'" << ::std::uint32_t(data.AsBool()) << "'";     break;
			case FIELD_UI8:     fmt << "'" << ::std::uint32_t(data.AsUint8()) << "'";    break;
			case FIELD_UI16:    fmt << "'" << ::std::uint32_t(data.AsUint16()) << "'";   break;
			case FIELD_UI32:    fmt << "'" << data.AsUint32() << "'";           break;
			case FIELD_UI64:    fmt << "'" << data.AsUint64() << "'";           break;
			case FIELD_I8:      fmt << "'" << ::std::int32_t(data.AsInt8()) << "'";      break;
			case FIELD_I16:     fmt << "'" << ::std::int32_t(data.AsInt16()) << "'";     break;
			case FIELD_I32:     fmt << "'" << data.AsInt32() << "'";            break;
			case FIELD_I64:     fmt << "'" << data.AsInt64() << "'";            break;
			case FIELD_FLOAT:   fmt << "'" << data.AsFloat() << "'";            break;
			case FIELD_DOUBLE:  fmt << "'" << data.AsDouble() << "'";           break;
			case FIELD_STRING:
			{
				std::string tmp = data.AsStr();
				conn_.get_db().EscapeString(tmp);
				fmt << "'" << tmp << "'";
				break;
			}
			case FIELD_NONE:                                                    break;
			}
		}

	}
}